#include<iostream>
#include<queue>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>

#define num 8

class blockqueue
{
private:
    std::queue<int> q;
    int cap;//capacity
    pthread_cond_t empty;
    pthread_cond_t full;
    pthread_mutex_t lock;
private:
    void lockqueue()
    {
        pthread_mutex_lock(&lock);
    }
    void unlockqueue()
    {
        pthread_mutex_unlock(&lock);
    }
    void productwait()
    {
        pthread_cond_wait(&full, &lock);
    }
    void consumewait()
    {
        pthread_cond_wait(&empty, &lock);
    }
    void notifyproduct()
    {
        pthread_cond_signal(&full);
    }
    void notifyconsume()
    {
        pthread_cond_signal(&empty);
    }
    bool IsEmpty()
    {
        return ( q.size() == 0 ? true : false );
    }
    bool IsFull()
    {
        return ( q.size() == cap ? true : false );
    }
public:
    blockqueue(int _cap = num):cap(_cap)
    {
        pthread_mutex_init(&lock, NULL);
        pthread_cond_init(&full, NULL);
        pthread_cond_init(&empty, NULL);
    }
    void pushdata(const int &data)
    {
        lockqueue();
        while(IsFull())
        {
            notifyconsume();
            std::cout <<"queue full, please consume data, product stop" << std::endl;
            productwait();
        }
        q.push(data);
        notifyconsume();
        unlockqueue();
    }
    void popdata( int &data)
    {
        lockqueue();
        while(IsEmpty()){
            notifyproduct();
            std::cout << "queue empty, notify product data, consume stop." << std::endl;
            consumewait();
        }
        data = q.front();
        q.pop();
        notifyproduct();
        unlockqueue();

    }
    ~blockqueue()
    {
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&full);
        pthread_cond_destroy(&empty);
    }
};

void *consumer(void *arg)
{
    blockqueue *bkq = (blockqueue*)arg;
    int data;
    while(1){
        bkq->popdata(data);
        std::cout << "consume data done!" << std::endl;
    }
}

void *producter(void *arg)
{
    blockqueue *bkq = (blockqueue*)arg;
    while(1)
    {
        srand((unsigned long)time(NULL));
        int data = rand()%1024;
        bkq->pushdata(data);
        std::cout << "product data done~" << data << std::endl;
        sleep(1);
    }
}


int main()
{
    blockqueue bq;

    pthread_t c, t;
    //(void*)&bq：是将BlockQueue类型的对象bq的地址转换为void*类型的指针，并作为参数传递给消费者线程函数。
    //这样，消费者线程函数就可以通过类型转换来访问和操作这个BlockQueue对象了。
    //在消费者线程函数consumer内部，这个void*类型的参数被转换回BlockQueue*类型，以便可以调用BlockQueue的方法：
    //  BlockQueue *bqp = (BlockQueue*)arg;
    pthread_create(&c, NULL, consumer, (void*)&bq);
    pthread_create(&t, NULL, producter, (void*)&bq);


    pthread_join(c, NULL);
    pthread_join(t, NULL);
    return 0;
}